/**
 * 类型相关的辅助函数
 */
import { strings } from "../../index";
import { ValidTypes, NonEmptyArray, NonEmptyObject, NonEmptyString } from "./types";

/**
 * 检测值是否为字符串类型
 * 
 * @param {unknown} value - 待检测的值
 * @param {boolean} nonEmpty - 可选，是否要求字符串非空
 * @returns {boolean} 如果是字符串且满足非空条件（如果指定）则返回true，否则返回false
 * @example
 * isString('hello') => true
 * isString('') => true
 * isString('', true) => false
 * isString(123) => false
 */
export function isString<T extends boolean = false>(
  value: unknown,
  nonEmpty: T = false as T
): value is T extends true ? NonEmptyString : string {
  return isTypeOf(value, 'string') && (!nonEmpty || (value as string).length > 0);
}

/**
 * 检测值是否为数字类型
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果是数字则返回true，否则返回false
 * @example
 * isNumber(123) => true
 * isNumber('123') => false
 * isNumber(NaN) => true
 */
export const isNumber = (value: unknown): value is number => isTypeOf(value, 'number');

/**
 * 检测值是否为布尔类型
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果是布尔值则返回true，否则返回false
 * @example
 * isBoolean(true) => true
 * isBoolean(0) => false
 */
export const isBoolean = (value: unknown): value is boolean => isTypeOf(value, 'boolean');

/**
 * 检测值是否为null
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果是null则返回true，否则返回false
 * @example
 * isNull(null) => true
 * isNull(undefined) => false
 */
export const isNull = (value: unknown): value is null => isTypeOf(value, 'null');

/**
 * 检测值是否为undefined
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果是undefined则返回true，否则返回false
 * @example
 * isUndefined(undefined) => true
 * isUndefined(null) => false
 */
export const isUndefined = (value: unknown): value is undefined => isTypeOf(value, 'undefined');

/**
 * 检测值是否为空
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果值为空则返回true，否则返回false
 * @example
 * isEmpty(null) => true
 * isEmpty(undefined) => true
 * isEmpty('') => true
 * isEmpty([]) => true
 * isEmpty({}) => true
 * isEmpty('hello') => false
 * isEmpty([1, 2, 3]) => false
 * isEmpty({ key: 'value' }) => false
 */
export const isEmpty = (value: unknown): boolean => {
  // 检查null和undefined
  if (value === null || value === undefined) {
    return true;
  }

  // 检查空字符串
  if (typeof value === 'string' && value.length === 0) {
    return true;
  }

  // 检查空数组
  if (Array.isArray(value) && value.length === 0) {
    return true;
  }

  // 检查对象
  if (typeof value === 'object') {
    // 对于Date和RegExp对象，不认为它们是空的
    if (value instanceof Date || value instanceof RegExp) {
      return false;
    }
    // 检查空对象
    return Object.keys(value).length === 0;
  }

  // 其他类型的非空值都返回false
  return false;
};

/**
 * 检测值是否为Symbol类型
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果是Symbol则返回true，否则返回false
 * @example
 * isSymbol(Symbol('key')) => true
 * isSymbol('symbol') => false
 */
export const isSymbol = (value: unknown): value is symbol => isTypeOf(value, 'symbol');

/**
 * 检测值是否为对象类型
 * 
 * @param {unknown} value - 待检测的值
 * @param {boolean} nonEmpty - 可选，是否要求对象非空（有至少一个自有属性）
 * @returns {boolean} 如果是对象且满足非空条件（如果指定）则返回true，否则返回false
 * @example
 * isObject({}) => true
 * isObject({}, true) => false
 * isObject({ key: 'value' }, true) => true
 * isObject([]) => false
 * isObject(null) => false
 */
export function isObject<T extends boolean = false>(
  value: unknown,
  nonEmpty: T = false as T
): value is T extends true ? NonEmptyObject : object {
  return isTypeOf(value, 'object') && (!nonEmpty || Object.keys(value as object).length > 0);
}

/**
 * 检测值是否为数组类型
 * 
 * @param {unknown} value - 待检测的值
 * @param {boolean} nonEmpty - 可选，是否要求数组非空（至少有一个元素）
 * @returns {boolean} 如果是数组且满足非空条件（如果指定）则返回true，否则返回false
 * @example
 * isArray([1, 2, 3]) => true
 * isArray([], true) => false
 * isArray({ length: 3 }) => false
 */
export function isArray<T extends boolean = false>(
  value: unknown,
  nonEmpty: T = false as T
): value is T extends true ? NonEmptyArray : any[] {
  return isTypeOf(value, 'array') && (!nonEmpty || (value as any[]).length > 0);
}

/**
 * 检测值是否为函数类型
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果是函数则返回true，否则返回false
 * @example
 * isFunction(() => {}) => true
 * isFunction('function') => false
 */
export const isFunction = (value: unknown): value is Function => isTypeOf(value, 'function');

/**
 * 检测值是否为正则表达式类型
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果是正则表达式则返回true，否则返回false
 * @example
 * isRegExp(/^test$/) => true
 * isRegExp(new RegExp('test')) => true
 * isRegExp('test') => false
 */
export const isRegExp = (value: unknown): value is RegExp => isTypeOf(value, 'regexp');

/**
 * 检测值是否为日期类型
 * 
 * @param {unknown} value - 待检测的值
 * @returns {boolean} 如果是日期则返回true，否则返回false
 * @example
 * isDate(new Date()) => true
 * isDate('2023-01-01') => false
 */
export const isDate = (value: unknown): value is Date => isTypeOf(value, 'date');

/**
 * 获取值的数据类型，返回 ValidTypes 中定义的类型字符串
 * 
 * @param {unknown} value - 任意类型的值
 * @returns {ValidTypes} 类型字符串，如 'string'、'array'、'regexp' 等
 * @example
 * getTypeOf('hello') => 'string'
 * getTypeOf([1,2,3]) => 'array'
 * getTypeOf(NaN) => 'number'
 */
export const getTypeOf = (value: unknown): ValidTypes => {
  if (value === null) return 'null';
  if (value === undefined) return 'undefined';

  const type = typeof value;
  if (type === 'string') return 'string';
  if (type === 'number') return 'number';
  if (type === 'boolean') return 'boolean';
  if (type === 'symbol') return 'symbol';
  if (type === 'bigint') return 'bigint';
  if (type === 'function') return 'function';

  if (Array.isArray(value)) return 'array';
  if (Object.prototype.toString.call(value) === '[object RegExp]') return 'regexp';
  if (Object.prototype.toString.call(value) === '[object Date]') return 'date';

  return 'object';
};

/**
 * 检查值是否为指定的类型
 * 
 * @param {unknown} value - 任意类型的值
 * @param {ValidTypes} type - 目标类型（ValidTypes 中定义的类型字符串）
 * @returns {boolean} 类型匹配结果
 * @example
 * isTypeOf(123, 'number') => true
 * isTypeOf(null, 'object') => false
 */
export function isTypeOf<T extends ValidTypes>(value: unknown, type: T): value is TypeOfGuard<T> {
  return getTypeOf(value) === type;
};

/**
 * 根据字符串类型名称获取对应的实际类型
 * 
 * @template T - 类型名称（ValidTypes 中定义的类型字符串）
 * @returns {T} 对应的实际类型
 * @example
 * type StringType = TypeOfGuard<'string'>; // string
 * type NumberType = TypeOfGuard<'number'>; // number
 */
export type TypeOfGuard<T extends ValidTypes> =
  T extends 'string' ? string :
  T extends 'number' ? number :
  T extends 'boolean' ? boolean :
  T extends 'null' ? null :
  T extends 'undefined' ? undefined :
  T extends 'symbol' ? symbol :
  T extends 'bigint' ? bigint :
  T extends 'object' ? object :
  T extends 'array' ? any[] :
  T extends 'function' ? Function :
  T extends 'regexp' ? RegExp :
  T extends 'date' ? Date :
  unknown;

/**
 * 完美解析对象为格式化字符串
 * 
 * 该函数能够将任意JavaScript值转换为格式化的字符串表示，支持复杂对象、循环引用检测、缩进控制等
 * 
 * @param {any} obj - 要格式化的对象或值
 * @param {number} indent - 可选，缩进空格数，默认为2
 * @param {number} maxDepth - 可选，最大递归深度，默认为10
 * @returns {string} 格式化后的字符串表示
 * 
 * @example
 * toString({ name: 'John', age: 30, hobbies: ['reading', 'gaming'] })
 * // 输出格式化后的对象字符串
 * 
 * toString([1, 2, { nested: 'object' }])
 * // 输出格式化后的数组字符串
 */
export function toString(
  obj: any,
  indent: number = 2,
  maxDepth: number = 10
): string {
  // 使用内部递归函数封装实现细节，避免暴露visited和currentDepth
  function formatInternal(
    value: any,
    currentIndent: number,
    remainingDepth: number,
    visitedSet: Set<any>,
    depth: number
  ): string {
    // 检查递归深度
    if (remainingDepth <= 0) {
      return '[Max Depth Reached]';
    }

    // 处理基本类型和null/undefined
    if (value === null) return 'null';
    if (value === undefined) return 'undefined';

    const typeName = getTypeOf(value);

    // 处理基本类型
    if (['string', 'number', 'boolean', 'symbol', 'bigint'].includes(typeName)) {
      if (typeName === 'string') {
        // 转义字符串中的特殊字符
        const escaped = value
          .replace(/\\/g, '\\\\')
          .replace(/"/g, '\\"')
          .replace(/\n/g, '\\n')
          .replace(/\r/g, '\\r')
          .replace(/\t/g, '\\t');
        return `"${escaped}"`;
      }
      return String(value);
    }

    // 处理特殊对象类型
    if (typeName === 'function') {
      return '[Function]';
    }
    if (typeName === 'regexp') {
      return (value as RegExp).toString();
    }
    if (typeName === 'date') {
      return `"${(value as Date).toISOString()}"`;
    }

    // 检测循环引用
    if (['object', 'array'].includes(typeName)) {
      if (visitedSet.has(value)) {
        return '[Circular Reference]';
      }
      visitedSet.add(value);
    }

    // 处理数组
    if (typeName === 'array') {
      if ((value as any[]).length === 0) {
        return '[]';
      }

      // 根据当前深度计算缩进：基础缩进单位 * (当前深度 + 1)
      const nextIndent = currentIndent * (depth + 1);
      const itemIndent = ' '.repeat(nextIndent);
      const closeIndent = ' '.repeat(currentIndent * depth); // 闭合括号与父级对齐

      const items = (value as any[]).map(item =>
        `${itemIndent}${formatInternal(item, currentIndent, remainingDepth - 1, visitedSet, depth + 1)}`
      ).join(',\n');

      return `[\n${items}\n${closeIndent}]`;
    }

    // 处理普通对象
    if (typeName === 'object') {
      const keys = Object.keys(value);

      if (keys.length === 0) {
        return '{}';
      }

      // 根据当前深度计算缩进：基础缩进单位 * (当前深度 + 1)
      const nextIndent = currentIndent * (depth + 1);
      const space = ' '.repeat(nextIndent);
      const closeIndent = ' '.repeat(currentIndent * depth); // 闭合花括号与父级对齐

      const properties = keys.map(key => {
        const propValue = value[key];
        return `${space}"${key}": ${formatInternal(propValue, currentIndent, remainingDepth - 1, visitedSet, depth + 1)}`;
      }).join(',\n');

      return `{\n${properties}\n${closeIndent}}`;
    }

    // 默认情况
    return String(value);
  }

  // 调用内部函数，传入初始参数
  return formatInternal(obj, indent, maxDepth, new Set(), 0);
}



/**
 * 分析数据并返回包含数据信息的对象
 * 
 * 该函数会分析传入的数据，返回一个包含原始数据、格式化内容、对应类型颜色、类型名称和日志方法的对象
 * 适用于调试和可视化数据信息的场景
 * 
 * @param {any} data - 要分析的数据，可以是任意类型
 * @returns {object} 包含数据信息的对象
 *    - {any} data - 原始数据
 *    - {string} content - 格式化后的显示内容
 *    - {string} color - 对应类型的颜色
 *    - {string} type - 数据的类型名称
 *    - {function} log - 打印数据信息的方法，带时间戳和类型标签
 *    - {function} html - 生成HTML字符串的方法，带颜色和类型标签
 * 
 * @example
 * const info = useInfo('Hello World');
 * console.log(info.content); // 输出: "Hello World"
 * console.log(info.color);   // 输出: "#4CAF50"
 * console.log(info.type);    // 输出: "string"
 * info.log();               // 在控制台打印带时间戳和类型标签的日志
 * 
 * const objInfo = useInfo({ name: 'John', age: 30 });
 * objInfo.log();           // 打印对象信息
 */
export function useInfo(data: any): {
  data: any,
  content: string;
  color: string;
  type: string;
  log: () => void;
  html: () => string;
} {
  // 所有ValidTypes类型的颜色映射表
  const typeColors: Record<string, string> = {
    string: '#4CAF50',    // 绿色
    number: '#2196F3',    // 蓝色
    boolean: '#FF5722',   // 深橙色
    null: '#9E9E9E',      // 灰色
    undefined: '#BDBDBD', // 浅灰色
    symbol: '#E91E63',    // 品红色
    object: '#607D8B',    // 深灰色
    array: '#009688',     // 青色
    function: '#673AB7',  // 深紫色
    regexp: '#FFEB3B',    // 黄色
    date: '#795548',      // 棕色
    bigint: '#3F51B5',    // 靛蓝色
  };

  // 获取完整类型名称
  const typeName = getTypeOf(data);
  // 获取类型对应的颜色
  const color = typeColors[typeName] || '#000000';
  const content = toString(data);

  // 返回包含内容、颜色、类型和log方法的对象
  return {
    data,
    content,
    color,
    type: typeName,
    log: function () {
      // 在调用log方法时生成时间戳
      const timestamp = Date.now();
      // 格式化时间戳为人类可读格式
      const formattedTime = new Date(timestamp).toLocaleString();
      // 命令行打印支持
      const typeLabel = `[${typeName.toUpperCase()}]`;
      const timeLabel = `[${formattedTime}]`;

      // 在浏览器console中输出带颜色的日志，包含时间戳
      // 使用%c占位符和CSS样式来设置颜色
      console.log(`%c${timeLabel} %c${typeLabel}`,
        `color: #666; font-weight: normal;`,
        `color: ${color}; font-weight: bold;`,
        content);
    },
    html: () => {
      return `<div style="color: ${color}; font-weight: bold;">${strings.chain(content).escapeHtml().toString()}</div>`
    }
  };
}